package com.mike;

import java.lang.reflect.Type;
import java.util.Date;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;

public class AutoJSON {

	private static interface TypeSerializer<TYPE> extends JsonSerializer<TYPE>, JsonDeserializer<TYPE>{}
	
	private static Gson getGson(double version){
		GsonBuilder builder = new GsonBuilder()
		.registerTypeAdapter(Date.class, new TypeSerializer<Date>(){
			@Override
			public JsonElement serialize(Date arg0, Type arg1,JsonSerializationContext arg2) {
				return arg0 != null ? new JsonPrimitive(arg0.getTime()) : JsonNull.INSTANCE;
			}
			@Override
			public Date deserialize(JsonElement arg0, Type arg1,JsonDeserializationContext arg2) throws JsonParseException {
				return arg0.isJsonPrimitive() ? new Date(arg0.getAsLong()) : null;
			}
		})
		.setVersion(version);
		return builder.create();
	}
	
	public static JSONObject toJson(Object obj) throws JsonSyntaxException{
		return toJson(obj, 0);
	}

	public static JSONObject toJson(Object obj, double version) throws JsonSyntaxException{
		if(obj == null)
			return new JSONObject();
		Gson gson = getGson(version);
		String jsoStr = gson.toJson(obj);
		try {
			return (JSONObject) new JSONParser().parse(jsoStr);
		} catch (ParseException e) {
			throw getExceptionFromParseException(jsoStr, e);
		}
	}
	
	public static <OBJ extends Object> OBJ fromJson(byte[] data, Class<OBJ> klass) throws JsonSyntaxException, NullPointerException{
		return fromJson(new String(data).trim(), 0, klass);
	}

	public static <OBJ extends Object> OBJ fromJson(String data, Class<OBJ> klass) throws JsonSyntaxException, NullPointerException{
		return fromJson(data, 0, klass);
	}
	
	public static <RETVAL> RETVAL fromJson(String jsonData, double version, Class<RETVAL> klass) throws JsonSyntaxException, NullPointerException{
		if(jsonData == null)
			throw new NullPointerException("json data cannot be null");
		if(klass == null)
			throw new NullPointerException("klass information cannot be null");
		JSONParser parser = new JSONParser();
		String data = jsonData.trim();
		try {
			parser.parse(new String(data));
		} catch (ParseException e) {
			throw getExceptionFromParseException(data, e);
		}
		return getGson(version).fromJson(jsonData, klass);
	}

	private static JsonSyntaxException getExceptionFromParseException(String jsoStr, ParseException e){
		int pos = e.getPosition();
		int start = pos - 20;
		int offset = pos - start;
		if(start < 0){
			start = 0;
			offset = pos;
		}
		int end = start + 40;
		if(end >= jsoStr.length())
			end = jsoStr.length() - 1;
		String substring = jsoStr.substring(start, end);
		if(start > 0){
			substring = "..." + substring;
			offset += 3;
		}
		if(end+3 < jsoStr.length())
			substring = substring + "...";
		System.err.println(substring);
		for(int i = 0; i < offset; i++)
			System.err.print(' ');
		System.err.println("^");
		return new JsonSyntaxException("ParseException - "+e.getMessage()+" - "+substring);
	}
	

}
